package firbi.base.com;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;

import com.google.android.maps.GeoPoint;

import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
import junit.framework.TestCase;

public class BusRoute_Unit_Test extends TestCase {

	protected void setUp() {
	}
	
	/**
	 * This method runs the appropriate test when called with a test name.
	 * 
	 * @param testName	The name of the test to run
	 */
	protected void runTest(String testName) {
		Log.i("test", "running test:"+testName);
		if(testName.equalsIgnoreCase("testGettingSetting"))
			testGettingAndSetting();
		if(testName.equals("testCache"))
			testCache();
			
	}
	
	protected void tearDown() {
		
	}
	
	/**
	 * This method test the getters and setters of BusRoute. It also test the cache inside BusRoute and tests that only one BusRoute with a given
	 * id every exists. This method runs once for each of the test cases in the tests array. To add a test case simply add another array to tests
	 * but make sure that you follow the format specified in the comment above tests. Also make sure that each array you add to tests represents a
	 * single BusRoute, so the id and agancy_id, routeNumber need to be from the same BusRoute. This method uses gettingSettingChecks and testUpdaters
	 * to test the different aspects of the getters and setters.
	 */
	@MediumTest
	public void testGettingAndSetting() {
							 //id			agency_id		routeNumber
		Object[][] tests = {{"0001_30", 	"0001", 		"30"},
							{"1111_46", 	"1111", 		"46"},
							{"01_-12", 		"01", 			"-12"}};
		
		BusRoute tmp = BusRoute.updateOrCreate(null, "11");
		assertNull("updateOrCreate did not return null when given a null id!", tmp);
		
		for(int i = 0; i < tests.length; i++){
			String id = ((String)tests[i][0]);
			String agency_id = ((String)tests[i][1]);
			String routeNumber = ((String)tests[i][2]);
			BusRoute check = BusRoute.updateOrCreate(id, routeNumber);
			gettingSettingChecks(check, i+" (type 1)", id, agency_id, routeNumber);
			testUpdaters(check, i+" (created with type 1)");
			
			id = "0"+id+"0";
			agency_id = "0"+agency_id;
			routeNumber+="0";
			HashMap attrs = new HashMap();
			attrs.put("id", id);
			attrs.put("routeNumber", routeNumber);
			check = BusRoute.updateOrCreate(attrs);
			gettingSettingChecks(check, i+" (type 2)", id, agency_id, routeNumber);
			testUpdaters(check, i+" (created with type 2)");
		}
	}
	
	/**
	 * This method is really just a helper method that tests that the updating of BusRoute is working. Using the route passed in this method
	 * will try and use the static update methods of BusRoute and make sure that those static updates effect this specific BusRoute.
	 * @param route		the route to run the test on
	 * @param iden		the string that is used when there is a problem to identify this run of the test
	 */
	@MediumTest
	public void testUpdaters(BusRoute route, String iden) {
		String routeNumber = "not a valid route number";
		String routeNumbero = route.getRouteNumber();
		
		BusRoute.updateOrCreate(route.getId(), routeNumber);
		gettingSettingChecks(route, "(updated with type 1)", route.getId(), route.getAgencyId(), routeNumber);
		
		HashMap attrs = new HashMap();
		attrs.put("id", route.getId());
		attrs.put("routeNumber", routeNumbero);
		BusRoute.updateOrCreate(attrs);
		gettingSettingChecks(route, "(updated with type 2)", route.getId(), route.getAgencyId(), routeNumbero);
	}
	
	/**
	 * This method tests the BusRoute cache. It does so by first creating a BusRoute with an id that could not exist in One Bus Away's back-end. It
	 * then uses the find method to try and find a BusRoute with the same odd id. If the cache is working correctly the same BusRoute that was created
	 * at the beggining of the test will be returned, if not find will most likely query One Bus Away and get an error or it will return some other
	 * BusRoute which will let us know the cache is not working.
	 */
	@MediumTest
	public void testCache() {
		String id = "0_-12";
		String routeNumber = "fake route Number";
		String agency_id = "0";
		
		BusRoute.updateOrCreate(id, routeNumber);
		BusRoute check = BusRoute.find("0_-12");
		
		assertNotNull("Cache failed, find(ID) returned null after a BusRoute was created with it's id equal to ID", check);
		gettingSettingChecks(check, "Cache Test", id, agency_id, routeNumber);
	}
	
	/**
	 * This method tests the methods that use getDirectionKeyedStopsServedByRoute() and that method itself. It does so by first getting a result from the
	 * method and then analyzing the results from the methods that use this method and make sure that all of the results correspond correctly. The methods
	 * tested include:
	 * getAllStops()
	 * getDestinations()
	 * getStops(String[] destination)
	 * The results of these methods is compared to the original call to getDirectionKeyedStopsServedByRoute(), as well as to the results of each other.
	 */
	@MediumTest
	public void testDKSSBRMethods() {
		
		BusRoute testRoute = BusRoute.find("0001_30");
		BusRoute invalidRoute = BusRoute.updateOrCreate("01_002345", "002345");
		Map<String[], BusStop[]> map = testRoute.getDirectionKeyedStopsServedByRoute();
		String[][] keyset = map.keySet().toArray(new String[1][1]);
		
		//Test BusRoute.getAllStops()
		BusStop[] allStops = testRoute.getAllStops();
		
		int count = 0;
		Iterator<String[]> i = map.keySet().iterator();
		while(i.hasNext()){
			String[] names = i.next();
			BusStop[] stops = map.get(names);
			if(names!=null && stops!=null)
			count += stops.length;
		}
		assertEquals("BusRoute.getAllStops() returned a different size than the total number of stops for this route!", count, allStops.length);
		
		for(int j = 0; j < keyset.length; j++){
			String[] ex_key = keyset[j];
			BusStop[] ex_stops = map.get(ex_key);
			for(int k = 0; k < ex_stops.length; k++){
				BusStop ex_stop = ex_stops[k];
				int l = 0;
				for(; l < allStops.length; l++){
					if(allStops[l].equals(ex_stop))
						break;
				}
				assertTrue("BusRoute.getAllStops() did not return all BusStops for the route! it missed: "+ex_stop.toString(), l<allStops.length);
			}
		}
		
		assertNull("BusRoute.getAllStops() did not return null for invalid BusRoute with id="+invalidRoute.getId(), invalidRoute.getAllStops());
		
		//Test BusRoute.getDestinations()
		String[][] destinations = testRoute.getDestinations();
		
		assertEquals("BusRoute.getDestinations() returned a different size than there are destinations for this route!", keyset.length, destinations.length);
		
		for(int j = 0; j < keyset.length; j++){
			String[] find_key = keyset[j];
			int k = 0;
			for(; k < destinations.length; k++){
				String[] check_key = destinations[k];
				if(find_key.length == check_key.length){
					int l = 0;
					for(; l < find_key.length; l++){
						String ex_string = find_key[l];
						int m = 0;
						for(; m < check_key.length; m++){
							if(check_key[m].equals(ex_string))
								break;
						}
						if(m==check_key.length){
							break;
						}
					}
					if(l==find_key.length)
						break;
				}
			}
			assertTrue("BusRoute.getDestinations() did not return all destinations for the route it missed:"+Arrays.toString(find_key), k<destinations.length);
		}
		
		assertNull("BusRoute.getDestinations() did not return null for invalid BusRoute with id="+invalidRoute.getId(), invalidRoute.getDestinations());
		
		//Test BusRoute.getStops(String[] destination)
		
		for(int j = 0; j < keyset.length; j++){
			BusStop[] actual = testRoute.getStops(keyset[j]);
			BusStop[] expected = map.get(keyset[j]);
			assertTrue("BusRoute.getStops("+Arrays.toString(keyset[j])+") did not return an array of the correct length!", actual.length == expected.length);
			if(actual.length == expected.length){
				int k = 0;
				for(; k < expected.length; k++){
					BusStop find_stop = expected[k];
					int l = 0;
					for(; l < actual.length; l++){
						if(actual[l].equals(find_stop))
							break;
					}
					if(l==actual.length)
						break;
				}
				assertTrue("BusRoute.getStops("+Arrays.toString(keyset[j])+") did not return the correct grouping of stops!", k==expected.length);
			}
		}
		
		assertNull("BusRoute.getStops(String[]) did not return null for invalid BusRoute with id="+invalidRoute.getId(), invalidRoute.getStops(new String[2]));
	}
	
	/**
	 * This method is a helper method that makes sure that the BusRoute passed has the passed in properties. If there are properties that do not
	 * match assert statements alert the testing suite.
	 * @param check			the BusRoute to test
	 * @param iden			the identification String used when an error occurs to specify a specific run of the test
	 * @param id			the id that should be found in check
	 * @param agency_id		the agancy_id that should be found in check
	 * @param routeNumber	the routeNumber that should be found in check
	 */
	public void gettingSettingChecks(BusRoute check, String iden, String id, String agency_id, String routeNumber) {
		assertTrue("TestData "+iden+": setting and getting id failed!", id.equals(check.getId()));
		if(agency_id!=null)
			assertTrue("TestData "+iden+": agency id inconsistent with id! got:"+check.getAgencyId()+" expected:"+agency_id, agency_id.equals(check.getAgencyId()));
		if(routeNumber!=null)
			assertTrue("TestData "+iden+": route number inconsistent with id! got:"+check.getRouteNumber()+" expected:"+routeNumber, routeNumber.equals(check.getRouteNumber()));
	}
	
}
